package com.lib76.socket;

import java.io.InputStream;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.lib76.socket.model.SocketInfo;
import com.lib76.socket.model.protocol.SocketProtocol;
import com.lib76.socket.model.protocol.SocketProtocolGroup;
import com.lib76.socket.model.protocol.SocketProtocolParameter;

/**
 * XML配置文件解析
 * @author 张浩
 * @version 1.0.0
 */
public class ConfigXmlResolver {
	
	public final static String NODE_ID = "id";
	public final static String NODE_TYPE = "type";
	public final static String NODE_REMOTE_IP = "remoteIp";
	public final static String NODE_REMOTE_PORT="remotePort";
	public final static String NODE_LOCAL_PORT ="localPort";
	public final static String NODE_SOKCET_MESSAGE_PROTOCOL_VERSION="messageProtocolVersion";
	public final static String NODE_SOKCET_PROTOCOL_TYPE="protocolType";
	public final static String NODE_MESSAGE_CONTROLLER_CLASS = "messageControlClass";
	public final static String NODE_STATUS_CONTROLLER_CLASS = "statusControlClass";
	public final static String NODE_INTERCEPTOR_PATH = "interceptorPath";
	
	public final static String XML_INTERCEPTOR = "interceptor";
	public final static String XML_SOCKET = "socket";
	
	public final static String NODE_PROTOCOL_ID = "id";
	public final static String NODE_PROTOCOL_VERSION = "version";
	public final static String NODE_PROTOCOL_CODE = "code";
	public final static String NODE_PROTOCOL_NAME = "name";
	public final static String NODE_PROTOCOL_TIMEOUT = "timeout";
	public final static String NODE_PROTOCOL_COMMAND = "command";
	public final static String NODE_PROTOCOL_TYPE = "type";
	
	public final static String NODE_PROTOCOL_PARAMETER_NAME = "name";
	public final static String NODE_PROTOCOL_PARAMETER_LENGTH = "length";
	public final static String NODE_PROTOCOL_PARAMETER_TYPE = "type";
	public final static String NODE_PROTOCOL_PARAMETER_VALUE = "value";
	
	public final static String XML_PROTOCOL = "protocol";
	public final static String XML_PROTOCOL_HEADER = "protocolHeader";
	public final static String XML_REQUEST = "request";
	public final static String XML_RESPONSE = "response";
	public final static String XML_PROTOCOL_PARAMETER = "parameter";
	
	public static boolean analyzeXmlConfig(InputStream in,Properties prop){
        try {
            if (in != null) {
                DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                Document doc = db.parse(in);
                Element root = doc.getDocumentElement();
                NodeList children = root.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    Node node = children.item(i);
                    if(XML_SOCKET.equals(node.getNodeName())){
                    	CommunicationServer.createAndSaveSocketEntity(analyzeNodeToSocketInfo(node,prop));
                    }
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
	}

	public static SocketInfo analyzeNodeToSocketInfo(Node node,Properties prop) throws DOMException, ClassNotFoundException{
		SocketInfo info = new SocketInfo();
		Node attr = node.getAttributes().getNamedItem(NODE_ID);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setId(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_TYPE);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setType(SocketTypeEnum.valueOf(attr.getNodeValue().trim()));
        }
        attr = node.getAttributes().getNamedItem(NODE_REMOTE_IP);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setRemoteIp(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_REMOTE_PORT);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	if(StringUtils.isNumeric(attr.getNodeValue().trim())){
        		info.setRemotePort(Integer.valueOf(attr.getNodeValue().trim()));
        	}else if(prop != null){
        		String temp = getPropValue(attr.getNodeValue().trim(),prop);
        		if(StringUtils.isNumeric(temp)){
        			info.setRemotePort(Integer.valueOf(temp));
        		}
        	}
        }
        attr = node.getAttributes().getNamedItem(NODE_LOCAL_PORT);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	if(StringUtils.isNumeric(attr.getNodeValue().trim())){
        		info.setLocalPort(Integer.valueOf(attr.getNodeValue().trim()));
        	}else if(prop != null){
        		String temp = getPropValue(attr.getNodeValue().trim(),prop);
        		if(StringUtils.isNumeric(temp)){
        			info.setLocalPort(Integer.valueOf(temp));
        		}
        	}
        }
        attr = node.getAttributes().getNamedItem(NODE_SOKCET_MESSAGE_PROTOCOL_VERSION);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setMessageProtocolVersion(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_SOKCET_PROTOCOL_TYPE);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setMessageProtocolType(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_MESSAGE_CONTROLLER_CLASS);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setMessageControlClass(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_STATUS_CONTROLLER_CLASS);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setStatusControlClass(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_INTERCEPTOR_PATH);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	info.setInterceptorPath(attr.getNodeValue().trim());
        }
        NodeList children = node.getChildNodes();
        if (children != null) {
            for (int j = 0; j < children.getLength(); j++) {
                Node child = children.item(j);
                if (XML_INTERCEPTOR.equals(child.getNodeName())) {
                	String temp = child.getTextContent();
                	if(StringUtils.isNotBlank(temp)){
                		info.getInterceptorClasses().add(temp.trim());
                	}
                }
            }
        }
        return info;
	}
	
	/**
	 * 解析报文协议
	 * @param cfgPath
	 * @param prop
	 * @return
	 */
	public static SocketProtocolGroup analyzeMessageProtocol(InputStream in,Properties prop){
        try {
            if (in != null) {
                DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                Document doc = db.parse(in);
                Element root = doc.getDocumentElement();
                SocketProtocolGroup protocolGroup = new SocketProtocolGroup();
                Node attr = root.getAttributes().getNamedItem(NODE_PROTOCOL_VERSION);
                if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
                	protocolGroup.setVersion(attr.getNodeValue().trim());
                }
                attr = root.getAttributes().getNamedItem(NODE_PROTOCOL_TYPE);
                if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
                	protocolGroup.setType(attr.getNodeValue().trim());
                }
                
                NodeList children = root.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    Node node = children.item(i);
                    if (XML_PROTOCOL_HEADER.equals(node.getNodeName())) {
                         NodeList grandsons = node.getChildNodes();
                         if (grandsons != null) {
                             for (int j = 0; j < grandsons.getLength(); j++) {
                                 Node grandson = grandsons.item(j);
                                 if(XML_PROTOCOL_PARAMETER.equals(grandson.getNodeName())){
                                	 protocolGroup.getHeaderParameters().add(analyzeNodeToMessageProtocolParameter(grandson,prop));
                                 }
                             }
                     	
                         }
                    } else if (XML_PROTOCOL.equals(node.getNodeName())) {
                    	SocketProtocol protocol = analyzeNodeToMessageProtocol(node,prop);
                    	if(StringUtils.isNotBlank(protocol.getCode())){
                    		protocolGroup.getProtocols().put(protocol.getCode(), protocol);
                    	}
                    }
                }
                return protocolGroup;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
	}
	
	private static SocketProtocol analyzeNodeToMessageProtocol(Node node,Properties prop){
		SocketProtocol protocol = new SocketProtocol();
		Node attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_CODE);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	protocol.setCode(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_NAME);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	protocol.setName(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_TIMEOUT);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	if(StringUtils.isNumeric(attr.getNodeValue().trim())){
        		protocol.setTimeout(Integer.valueOf(attr.getNodeValue().trim()));
        	}
        }
        NodeList children = node.getChildNodes();
        if (children != null) {
            for (int i = 0; i < children.getLength(); i++) {
                Node child = children.item(i);
                if (XML_REQUEST.equals(child.getNodeName())) {
                	attr = child.getAttributes().getNamedItem(NODE_PROTOCOL_COMMAND);
                    if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
                    	protocol.setRequestCommand(attr.getNodeValue().trim());
                    }
                    attr = child.getAttributes().getNamedItem(NODE_PROTOCOL_TYPE);
                    if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
                    	protocol.setRequestType(attr.getNodeValue().trim());
                    }
                    NodeList grandsons = child.getChildNodes();
                    if (grandsons != null) {
                        for (int j = 0; j < grandsons.getLength(); j++) {
                            Node grandson = grandsons.item(j);
                            if(XML_PROTOCOL_PARAMETER.equals(grandson.getNodeName())){
                            	protocol.getRequestParameters().add(analyzeNodeToMessageProtocolParameter(grandson,prop));
                            }
                        }
                	
                    }
                }else if (XML_RESPONSE.equals(child.getNodeName())) {
                	attr = child.getAttributes().getNamedItem(NODE_PROTOCOL_COMMAND);
                    if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
                    	protocol.setResponseCommand(attr.getNodeValue().trim());
                    }
                    attr = child.getAttributes().getNamedItem(NODE_PROTOCOL_TYPE);
                    if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
                    	protocol.setResponseType(attr.getNodeValue().trim());
                    }
                    NodeList grandsons = child.getChildNodes();
                    if (grandsons != null) {
                        for (int j = 0; j < grandsons.getLength(); j++) {
                            Node grandson = grandsons.item(j);
                            if(XML_PROTOCOL_PARAMETER.equals(grandson.getNodeName())){
                            	protocol.getResponseParameters().add(analyzeNodeToMessageProtocolParameter(grandson,prop));
                            }
                        }
                	
                    }
                }
            }
        }
		return protocol;
	}
	
	private static SocketProtocolParameter analyzeNodeToMessageProtocolParameter(Node node,Properties prop){
		SocketProtocolParameter parameter = new SocketProtocolParameter();
		Node attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_PARAMETER_NAME);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	parameter.setName(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_PARAMETER_TYPE);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	parameter.setType(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_PARAMETER_VALUE);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	parameter.setValue(attr.getNodeValue().trim());
        }
        attr = node.getAttributes().getNamedItem(NODE_PROTOCOL_PARAMETER_LENGTH);
        if (attr != null && StringUtils.isNotBlank(attr.getNodeValue())) {
        	if(StringUtils.isNumeric(attr.getNodeValue().trim())){
        		parameter.setLength(Integer.valueOf(attr.getNodeValue().trim()));
        	}
        }
		return parameter;
	}
	
	private static String getPropValue(String value,Properties prop) throws ClassNotFoundException {
        Pattern pat = Pattern.compile("^\\$\\{([^\\}]*)\\}$");
        Matcher mat = pat.matcher(value);
        if (mat.find()) {
            return prop.getProperty(mat.group(1)).trim();
        }
        return null;
    }
}
